package edu.emory.cci.dcmdocstore;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.UnknownHostException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;

import org.bson.types.ObjectId;
import org.json.JSONException;
import org.json.JSONObject;

import com.mongodb.AggregationOutput;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.gridfs.GridFS;
import com.mongodb.gridfs.GridFSDBFile;
import com.mongodb.gridfs.GridFSInputFile;
import com.mongodb.util.JSON;
public class MongoDB implements Runnable{
	public static Queue<String[]> jsonQueue;
	public static Queue<String> pathQueue;
	public static DBCursor cursor;
	private Mongo mongo;
	private DB db ;
	private Dcm2Other dcmTool = new Dcm2Other();
	private GridFS fs;
	private int workType = 0;
	private String pathOrcoll;
	final static int FILELISTREADER = 1;
	final static int JSONPARSER = 2;
	final static int UPLOADER = 3;
	final static int FILEWRITER = 4;
	public static int filenumber = 0;
	public static String[] worker = {"","FILELISTREADER","JSONPARSER","UPLOADER","FILEWRITER"};
	public boolean stopped = false;
	public MongoDB()
	{
			try {
				mongo = new Mongo("pais1.cci.emory.edu", 27031);
			} catch (UnknownHostException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			db = mongo.getDB("dicomdb");
		    fs = new GridFS( db );
		    pathOrcoll = "dicom";
	}
	
	
	public MongoDB(int type)
	{
		    workType = type;
			
	}
	public MongoDB(int type, String path)
	{
		    workType = type;
		    pathOrcoll = path;
	}
	
	public MongoDB(int type, String database,String coll,String host,int port)
	{
		    workType = type;
			try {
				mongo = new Mongo(host, port);
			} catch (UnknownHostException e) {
				System.out.println("unknown host!");
				e.printStackTrace();
			}
			db = mongo.getDB(database);
		    fs = new GridFS( db );
		    pathOrcoll = coll;
	}
	
	@Override
	public void run() {
		long start = System.currentTimeMillis();
        if(workType==FILELISTREADER)//read filepath
        {
           pathQueue = new LinkedList<String>();
           jsonQueue = new LinkedList<String[]>();
           readFilelist(new File(pathOrcoll));
        }
        else if(workType==JSONPARSER)
        {
        	while(pathQueue==null)
        	{
        		try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
        		
        	}
        	parseJson(pathOrcoll);
        }
        else if(workType==UPLOADER)
        {
        	
        	while(jsonQueue==null)
        	{
        		try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
        		
        		
        	}
        	insertDicom(pathOrcoll);
        	     	
        }else
        {
        	writeCursor();
        }
        
        long end = System.currentTimeMillis();
        System.out.println("my workType is:  "+worker[workType]+" I'm died. totally use : " +(end-start)+"  milliseconds");	
	}
	
	public void readFilelist(File file)
	{
		int pathsize = 0;
   		if(file.isDirectory())
   		{
   		   File[] filelist = file.listFiles();
   		   for(File f:filelist)
   			readFilelist(f);
   		}
   	    else if(file.isFile()&&file.getName().endsWith(".dcm"))
   	    {
   	    	synchronized(pathQueue)
   	       {
   	    	pathQueue.offer(file.getAbsolutePath());
   	       }
   	    	
   	    	if(pathsize>1000)
   		    {
   		    	try {
   					Thread.sleep((pathsize)/1000);
   				} catch (InterruptedException e) {
   				}
   		    }
           
   	    }
   		if(file.getAbsolutePath().equalsIgnoreCase(pathOrcoll))
   			pathQueue.offer("stop!");
   		//if(filenumber%10000==0)
		//System.out.println("file number now is: "+(filenumber++));
	}
	
	public void parseJson(String collName)
	{
		DBCollection collection = db.getCollection(collName);
		String tmppath = null;
		int jsonsize=0;
		String[] pathandjson = new String[2];
		while(true)
		{
		
		jsonsize = jsonQueue.size();
	    
	    if(jsonsize>1000)
		  {
		   try {
					Thread.sleep(1000);
					System.out.println("jsonsize is too large, I need to take a rest!");
					continue;
				} catch (InterruptedException e) {
				}
		  }
		synchronized(pathQueue)
		{
			tmppath = pathQueue.peek();
			if(tmppath==null)continue;
			if(tmppath.equalsIgnoreCase("stop!"))
				{
				    if(stopped == false)
				      {
				    	synchronized(jsonQueue)
				    	{
				    	 jsonQueue.offer(new String[]{tmppath,""});
				    	}
				    	 stopped = true;
				      }
				    return;
				}
			else tmppath = pathQueue.poll();
		}
		
		if(tmppath==null)
		{
			try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				continue;
			}
			continue;
		}
		String json = dcmTool.dcm2Json(tmppath);
		/*
		DBObject dbObject = (DBObject)JSON.parse(json);
		DBObject sopob = new BasicDBObject("SOPInstanceUID",dbObject.get("SOPInstanceUID"));
		try{
			if(collection.findOne(sopob)!=null)
			{
				System.out.println(tmppath+" already exists in MongoDB "+ (MongoDB.count++));
				continue;		
			}
		}catch(Exception e)
		{
			System.out.println("exception in test the existence of "+pathandjson[0]);
		}
		*/
		pathandjson[0] = tmppath;
    	pathandjson[1] = json;
	    synchronized(jsonQueue)
	    {
	    	
	    	jsonQueue.offer(pathandjson);
	    }
	    
	  }
	 
}
	
	public void insertDicom(String collName)
	{
		DBCollection collection = db.getCollection(collName);
		String[] pathandjson = null;
		while(true)
		{
		synchronized(jsonQueue)
		{
			pathandjson = jsonQueue.peek();
			if(pathandjson!=null&&pathandjson[0].equalsIgnoreCase("stop!")){
				return;
			}
			else pathandjson=jsonQueue.poll();
		}
		if(pathandjson==null)
		{
			try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				continue;
				}
			continue;
		}

		DBObject dbObject = (DBObject)JSON.parse(pathandjson[1]);
		
		
		GridFSInputFile in;
		try {
			in = fs.createFile( new FileInputStream(pathandjson[0]),pathandjson[0]);
			dbObject.put("file_path", in.getFilename());
			dbObject.put("files_id", in.getId());
			in.save();
	        collection.insert(dbObject);
	        //if(MongoDB.count%10==0)
	        
	        System.out.println(pathandjson[0]+" is inserted "+(MongoDB.count++));
		}catch(Exception e)
		{
			e.printStackTrace();
			System.out.println("insert file "+pathandjson[0]+" failed!");
		}
		
	  }
	}
	
	public void writeCursor()
	{
		
		long start  = System.currentTimeMillis();
		DBObject obj;
		int counter=0;
		String filepath = null;
		while(true)
		 {
			 synchronized(cursor)
			 {
				 if(cursor.hasNext())
					 obj = cursor.next();
				 else break;
			 }
			 counter++;
			 filepath = obj.get("file_path").toString();
			 filepath = filepath.substring(filepath.indexOf("\\"),filepath.length() );
			 //System.out.println(Thread.currentThread()+" writing file "+filepath);
			 savaFileByID(obj.get("files_id"), "f:"+filepath);		 
		 }
		 long end = System.currentTimeMillis();
         System.out.println("write " + counter+" files, totally cost "+(end-start)+" millisecond");
	}

	
	public DBCursor basicQuery(String collName,String key,Object value){
		
		return db.getCollection(collName).find(new BasicDBObject(key,value));

	}
	public DBCollection getCollection(String collumn)
	{
		return db.getCollection(collumn);	
	}
	
	public void clearColl(String collName)
	{
		DBCollection collection = db.getCollection(collName);
		DBCursor cursor = collection.find();
		 while(cursor.hasNext())
		 {
			 DBObject dbobject = cursor.next();
			 fs.remove((ObjectId)dbobject.get("files_id"));
			 collection.remove(dbobject);
		 }
		 DBCursor fc = fs.getFileList();
		 while(fc.hasNext())
		 {
			 fs.remove(fc.next());
		 }
		 
	}
	
	public  void savaFileByID(Object object,String path)
	{
		GridFSDBFile out =  fs.findOne( new BasicDBObject( "_id" , object ) );	
		 //Save loaded image from database into new image file
        FileOutputStream outputImage;
        File parentDir = new File(path.substring(0,path.lastIndexOf("\\")));
        parentDir.mkdirs();
		try {
			outputImage = new FileOutputStream(path);
			out.writeTo( outputImage );
	        outputImage.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	

	
	public static int count = 1;
	public static void main(String[] args) {
			//inputFromDir("H:\\dicom\\Virtual Colonoscopy\\part2");
			//inputFromDir("f:\\test3");
		   Long starttime = System.currentTimeMillis();
			query2();
			Long endtime = System.currentTimeMillis();
			System.out.println(endtime-starttime);
	}
	/////////////////////////////////////////////////////////////////////////
	public static void query()
	{
		//number of writer = 1, write 1417 files, totally cost 138396 millisecond
		//number of writer = 2, write 1417 files, totally cost 68907 millisecond
		//number of writer = 5, write 1417 files, totally cost 62588 millisecond
		//number of writer = 10, write 1417 files, totally cost 61519 millisecond
		int numberofWriter = 2;
		MongoDB mongo = new MongoDB();
		DBCollection c = mongo.getCollection("dicom");
	    cursor = c.find(new BasicDBObject("PatientAge",new BasicDBObject("$gt","089Y"))).sort(new BasicDBObject("PatientAge",-1));
	    //new BasicDBObject("_id",new ObjectId("516dc0b375bdd469ce1a4645")));   
	    for(int i = 0;i<numberofWriter;i++)
	    {
	    	Thread thread = new Thread(new MongoDB(FILEWRITER,"dicomdb","dicom","pais1.cci.emory.edu",27031));
	    	thread.start();
	    }
	}
	public static void query2()
	{
	    MongoDB mongo = new MongoDB();
	    DBCollection c = mongo.getCollection("dicom");
	    AggregationOutput aggre = c.aggregate(new BasicDBObject("$group",new BasicDBObject("_id",new BasicDBObject("PatientAge","$PatientAge")).append("number",new BasicDBObject("$sum",1))),new BasicDBObject("$sort",new BasicDBObject("number",-1)),new BasicDBObject("$limit",5));
	    Iterator<DBObject> it = aggre.results().iterator();
	    while(it.hasNext())
	    {
		    System.out.println(it.next().toString());
	    }
	}
	///////////////////////////////////////////////////////////////////////////////////
	public static void inputFromDir(String path)
	{
		Thread fileReader;
		fileReader = new Thread(new MongoDB(FILELISTREADER,path));
		fileReader.start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e1) {
		}
		for(int i = 0;i<8;i++)
		{
		Thread worker = new Thread(new MongoDB(JSONPARSER,"dicomdb","dicom","pais1.cci.emory.edu",27031));
		worker.start();
		}
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Thread uploader = new Thread(new MongoDB(UPLOADER,"dicomdb","dicom","pais1.cci.emory.edu",27031));
		uploader.start();
		
	}
	

	public static byte[] readFileByte(File file)
	{
		byte[] bin = null;
		try {
			InputStream in = new FileInputStream(file);
			bin = new byte[in.available()];
			in.read(bin);
			in.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	    return bin;
	}
	
	public static byte[] readFileByte(String file)
	{
		return readFileByte(new File(file));
	}
	
	
	/*
	 	public void insertDicomsGribFS(File file,String collName)
	{
		DBCollection collection = db.getCollection(collName);
		if(file.isDirectory())
			{
			   File[] filelist = file.listFiles();
			   for(File f:filelist)
			       insertDicomsGribFS(f,collName);
			}
		else if(file.isFile()&&file.getName().endsWith(".dcm"))
		{
			String json = dcmTool.dcm2Json(file).toString();
			DBObject dbObject = (DBObject)JSON.parse(json);
			
		    GridFSInputFile in = fs.createFile( readFileByte(file) );
		    in.save();
		    dbObject.put("file_id", new ObjectId(in.getId().toString()));
            collection.insert(dbObject);	
            System.out.println(MongoDB.count++);
		}
		
	}
	
	public void insertDicomsBase64(File file,String collName)
	{
		DBCollection collection = db.getCollection(collName);
		if(file.isDirectory())
		{
		   File[] filelist = file.listFiles();
		   for(File f:filelist)
		       insertDicomsBase64(f,collName);
		}
	   else if(file.isFile()&&file.getName().endsWith(".dcm"))
	  {
		String json = dcmTool.dcm2Json(file).toString();
		DBObject dbObject = (DBObject)JSON.parse(json);
	    dbObject.put("file_Object", readFileBase64(file.getAbsolutePath()));
        collection.insert(dbObject);	
        System.out.println(MongoDB.count++);
	  }
	}
		public static String readFileBase64(String filepath)
	{
		String result = null;
	    try {
			InputStream in = new FileInputStream(filepath);
			byte[] bin = new byte[in.available()];
			in.read(bin);
			in.close();
		   result =  new BASE64Encoder().encode(bin);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	    return result;
	    
	}
	
	public static void writeBase64(String content, String filepath)
	{
		try {
			OutputStream out = new FileOutputStream(filepath);
			byte[] bout = new BASE64Decoder().decodeBufferToByteBuffer(content).array();
			out.write(bout);
			out.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}	
	}
	 */
}